#include "debugTools.h"
#include "main.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>//支持sprintf，vsprintf函数

#define log_isBound(channel) (log_d[channel] != NULL)

#define BUF_MAX 20
/*
使用说明：
@$c:0,1#\n
代表通道1发送指令1,包头@$,包尾#，最后加\n
begin2里面初始化	my_dt_init();
call_reg(0, PID_change);
call_reg(1, voice_light_cal);注册通道
void PID_change(int i,const char* string){
  if(sscanf(string, "%f,%f,%f", &kp,&ki,&kd) == 3) {
    usart_printf("%.2f,%.2f,%.2f\n",kp,ki,kd);
  }
}
while里面debugHandler();
打印数据usart_printf("Voice and light has done!");
*/
float *para_d[BUF_MAX];   // 参数指针列表
float para_back[BUF_MAX]; // 缓存，防止局部变量有大量无效值
int para_status[BUF_MAX]; // 更新状态

float *log_d[BUF_MAX];                       // 输出指针列表
int log_num;                                 // 输出个数 (可以去掉?)
char log_tail[4] = {0x00, 0x00, 0x80, 0x7f}; // 包尾
int log_stat;                                // 是否使能输出

char revBuf[100], revBuf_bit;

debugSend_fn_t debugSend_fn;
call_t call_d[BUF_MAX];   // 函数指针列表
int call_status[BUF_MAX]; // 触发状态
char *call_params[BUF_MAX]; // 每个通道的字符串参数存储

// todo: 可以使用DMA优化

extern UART_HandleTypeDef huart1;

void usart_printf(char *format,...)
{
	char String[100];		 //定义输出字符串
	va_list arg;			 //定义一个参数列表变量va_list是一个类型名，arg是变量名
	va_start(arg,format);	 //从format位置开始接收参数表放在arg里面
	
	//sprintf打印位置是String，格式化字符串是format，参数表是arg，对于封装格式sprintf要改成vsprintf
	vsprintf(String,format,arg);
	va_end(arg);			 //释放参数表
  HAL_UART_Transmit(&huart1, (const uint8_t *)String, strlen(String), 0xffff);
	
}

void my_dt_send(const uint8_t *buf, int len) {
  HAL_UART_Transmit(&huart1, buf, len, 0xffff);
}

void my_paramInit(void) {
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&revBuf_bit, 1);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { // 接收完成callback
  param_uartIT_handler(revBuf_bit);
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&revBuf_bit, 1); // 开始下一次读取
}

void my_dt_init(void) {
  char buf[50];
  sprintf(buf, "dt_aaa\n");
  HAL_UART_Transmit(&huart1, (const uint8_t *)buf, strlen(buf), 0xffff);
  debugInit(my_dt_send, my_paramInit);
}

void debugInit(debugSend_fn_t fn, void (*paramInitfn)()) {
  debugSend_fn = fn;
  paramInitfn();
  for (int i = 0; i < BUF_MAX; i++) {
    call_params[i] = NULL; // 初始化字符串存储为NULL
  }
}

void debugHandler(void) {
  if (log_stat == 1) {
    int j = 0;
    for (int i = 0; i < log_num; i++) {
      while (!log_isBound(j)) // 过滤空位置
        j++;
      debugSend_fn((const uint8_t *)log_d[j], sizeof(float));
      j++;
    }
    debugSend_fn((const uint8_t *)log_tail, 4);
  }

  for (int i = 0; i < BUF_MAX; i++) {
    if (call_status[i]) {
      if (call_d[i] != NULL) {
        call_d[i](i, call_params[i]); // 传递字符串参数
      }
      call_status[i] = 0;
    }
  }
}

void call_reg(int i, call_t cb_f) {
  if (i >= BUF_MAX)
    return;
  call_d[i] = cb_f;
}

void call_del(int i) {
  if (i >= BUF_MAX)
    return;

  // 释放字符串参数内存
  if (call_params[i] != NULL) {
    free(call_params[i]);
    call_params[i] = NULL;
  }

  call_d[i] = NULL;
}

void log_setStat(int i) {
  if (i)
    log_stat = 1;
  else
    log_stat = 0;
}

int log_autoBinding(float *data) {
  int j = 0;
  while (log_isBound(j)) { // 获取闲置索引
    j++;
    if (j == BUF_MAX) // 越界
      return -1;
  }

  log_setBinding(j, data);
  return j;
}

void log_setBinding(int channel, float *data) {
  if (data == NULL || channel >= BUF_MAX)
    return;

  if (!log_isBound(channel)) // 不是覆盖时才加一
    log_num++;

  log_d[channel] = data;
}

void log_delBinding(int channel) {
  if (log_isBound(channel)) // 有数据时才减一
    log_num--;

  log_d[channel] = NULL;
}

// 启动串口接收
void param_init(void) {}

// 绑定数据
void param_setBinding(int channel, float *data) {
  // 对于常更新的局部变量，有可能绑定前就有数据进入，绑定时把数据传入。
  if (para_d[channel] == NULL &&
      para_status[channel] == 1) { // 不是覆盖（排除有数据且status=1） 且 有缓存
    *data = para_back[channel];
    para_status[channel] = 0;
  }

  para_d[channel] = data;
}

// 取消绑定（局部变量要注意使用
void param_delBinding(int channel) {
  para_d[channel] = NULL;
  para_status[channel] = 0;
}

char *my_strdup(const char *s) {
    if (s == NULL) return NULL;
    size_t len = strlen(s) + 1;
    char *dup = (char *)malloc(len);
    if (dup) {
        memcpy(dup, s, len);
    }
    return dup;
}


// 参数串口中断处理
void param_uartIT_handler(char buf) {
  static int i = -1, waiting_state = 0, reading_state = 0;
  int channel;
  float data;


  if(i>=0) { // reading
    revBuf[i++] = buf;
  }

  // at any time
  switch (waiting_state) {
    case 0:
      if(buf == '@') {
        waiting_state = 1;
      }
      break;

    case 1:
      waiting_state = 0; // only come in for once
      if(buf == '$') { // get heading: @$
        i = 0; // start reading
      }
      break;
  }
  
  if(i>=0) { // while reading
    switch(reading_state) {
      case 0:
        if(buf == '#') {
          reading_state = 1;
        }
        break;

      case 1:
        if(buf == '\n') { // get ending: #\n
          reading_state = 2; // goto decode

          revBuf[i-2] = '\n';
          revBuf[i-1] = '\0';

          i = -1; // goto waiting
        }
        else {
          reading_state = 0; // go back
        }
        break;
    }
  }

  if(reading_state != 2) // decoding mode
    return;

  reading_state = 0;

  // 参数解析
  if (sscanf(revBuf, "%d:%f\n", &channel, &data) == 2) {
    if (para_d[channel] != NULL) {
      *para_d[channel] = data;
    } else {
      para_back[channel] = data;
    }
    para_status[channel] = 1;
  } else{
    int offset, n;
    char ch;
    n=sscanf(revBuf, "c:%d,%n%c", &channel, &offset, &ch);
    if ( n == 1 || n==2 ) {
      if (channel < BUF_MAX) {
        call_status[channel] = 1;

        // 释放旧字符串
        if (call_params[channel] != NULL) {
          free(call_params[channel]);
        }

        if(n==1) {
          call_params[channel] = NULL;
          return;
        }

        // 使用 strdup 存储新字符串
        call_params[channel] =my_strdup((char*) (revBuf + offset));
        if (call_params[channel] == NULL) {
          printf("Memory allocation failed for call_params[%d]\n", channel);
        }
      }
    }
  }
}

int param_channelStatus(int channel) {
  int ret = para_status[channel];
  para_status[channel] = 0;
  return ret;
}
